/* 
 * File:   main.cpp
 * Author: solozobov
 *
 * Created on May 7, 2013, 11:00 PM
 */

#include <iostream>
#include <string>
#include <stdio.h>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <boost/filesystem.hpp>
#include <boost/thread/thread.hpp>
#include <boost/date_time.hpp>

using namespace std;
using namespace std::tr1;
using namespace boost;
using namespace boost::this_thread;
using namespace boost::asio;
using namespace boost::asio::ip;
//
using namespace boost::filesystem;
using namespace boost::system;
using namespace boost::interprocess;
using namespace boost::posix_time;

int send(char* host, unsigned short port, const char* file_path) {
	try {
		io_service _io_service;
		tcp::socket _socket(_io_service, tcp::v4());
		error_code _error_code;
		tcp::endpoint target_address(address_v4::from_string(host), port);
		_socket.connect(target_address);
		
		uintmax_t region_size = 5 * 1024 * 1024;
		uintmax_t _file_size = file_size(file_path);
		file_mapping _file_mapping(file_path, read_only);
		
		size_t written = 0;
		ptime start = microsec_clock::universal_time();
		for (uintmax_t position = 0; position < _file_size; position += region_size) {
			mapped_region _mapped_region(_file_mapping, read_only, position, min(region_size, _file_size - position));
			written += write(_socket, buffer(_mapped_region.get_address(), _mapped_region.get_size()), transfer_all(), _error_code);
		}
		ptime finish = microsec_clock::universal_time();
		
		cout << "Sent " << written << " of " << _file_size << " in " << finish - start << endl;
		_socket.close();
		return 0;
	} catch (std::exception& e) {
		cerr << e.what() << endl;
		return 1;
	}
}

int receive(unsigned short port, const char* file_name) {
	try {
		io_service _io_service;
		tcp::socket _socket(_io_service);

		path file_path(file_name);
		if (exists(file_path)) {
			remove(file_path);
		}

		const int buffer_size = 5 * 1024 * 1024;
		char _buffer[buffer_size];
		FILE* file = fopen(file_name, "wb");
		
		tcp::endpoint _endpoint(tcp::v4(), port);
		tcp::acceptor _acceptor(_io_service, _endpoint);
		error_code _error_code;
	
		_acceptor.accept(_socket);
		cout << "Request from " << _socket.remote_endpoint() << "\n";
		
		size_t received = 0;
		size_t portion = 0;
		ptime start = microsec_clock::universal_time();
		do {
			portion = read(_socket, buffer(_buffer, buffer_size), _error_code);
			received += portion;
			fwrite(_buffer, 1, portion, file);
		} while(portion > 0);
		ptime finish = microsec_clock::universal_time();
		
		cout << "Received " << received << " in " << finish - start << endl;
		_socket.close();
		fclose(file);
		return 0;
	} catch (std::exception& e) {
		cerr << e.what() << endl;
		return 1;
	}
}

int check(char* file_name1, char* file_name2) {
	cout << "Checking ... ";
	cout.flush();
	FILE* file1 = fopen(file_name1, "rb");
	FILE* file2 = fopen(file_name2, "rb");
	const unsigned int buffer_size = 2 * 1024 * 1024;
	char buffer1[buffer_size];
	char buffer2[buffer_size];

	do {
		size_t red1 = fread(buffer1, 1, buffer_size, file1);
		size_t red2 = fread(buffer2, 1, buffer_size, file2);
		if (feof(file1) != feof(file2) || red1 != red2 || memcmp(buffer1, buffer2, red1)) {
			cout << "ERROR!!!" << endl;
			return -1;
		}
	} while (!feof(file1) && !feof(file2));
	
	cout << "OK" << endl;
	return 0;
}

int sendTest(char* destination, int port, int argc, char** argv) {
	io_service _io_service;
	tcp::endpoint target_address(address_v4::from_string(destination), port);
	tcp::socket _socket(_io_service, tcp::v4());
	error_code _error_code;
	
	if (argc > 0) {
		int route[argc + 1];
		((char*) route)[0] = 1;
		((char*) route)[1] = 131;
		((char*) route)[2] = 3 + argc * 4;
		((char*) route)[3] = 4;
		for (int i = 0; i < argc; i++) {
			route[i + 1] = inet_addr(argv[i]);
		}
		
		if (setsockopt(_socket.native_handle(), IPPROTO_IP, IP_OPTIONS, route, (argc + 1 ) * 4) < 0) {
			perror("can't set socket option");
		}
	}
	
	_socket.connect(target_address);
	std::cout << write(_socket, buffer("Test", 4), _error_code) << std::endl;
	
	_socket.close();
	return 0;
}

int main2(int argc, char** argv) {
	if (argc >= 2) {
		string option(argv[1]);
		if (argc >= 5 && option.compare("-s") == 0) {
			return send(argv[2], (unsigned short) atoi(argv[3]), argv[4]);
		} else if (argc >= 4 && option.compare("-r") == 0) {
			return receive((unsigned short) atoi(argv[2]), argv[3]);
		} else if (argc >= 4 && option.compare("-t") == 0) {
			return sendTest(argv[2], atoi(argv[3]), argc - 4, argv + 4);
		} else if (argc >= 4 && option.compare("-c") == 0) {
			return check(argv[2], argv[3]);
		}
	}
	cout << "Argument must be:" << endl << "\"-s HOST POST FILE_NAME\" for sender" << endl << "\"-r PORT FILE_NAME\" for receiver" << endl << "\"-c FILI_1_NAME FILE_2_NAME\" for receiver" << endl << "\"-t HOST PORT ROUTE_HOST_1 ...\" to test loose source send" << endl;
	return 1;
}

int main(int argc, char** argv) {
	std::tr1::unordered_multimap<unsigned int, unsigned int> graph;
	graph.insert(std::make_pair<unsigned int, unsigned int>(1,2));
	graph.insert(std::make_pair<unsigned int, unsigned int>(2,1));
	graph.insert(std::make_pair<unsigned int, unsigned int>(1,3));
	graph.insert(std::make_pair<unsigned int, unsigned int>(3,1));
	graph.insert(std::make_pair<unsigned int, unsigned int>(3,5));
	graph.insert(std::make_pair<unsigned int, unsigned int>(5,3));
	graph.insert(std::make_pair<unsigned int, unsigned int>(2,4));
	graph.insert(std::make_pair<unsigned int, unsigned int>(4,2));
	graph.insert(std::make_pair<unsigned int, unsigned int>(1,4));
	graph.insert(std::make_pair<unsigned int, unsigned int>(4,1));
	graph.insert(std::make_pair<unsigned int, unsigned int>(1,7));
	graph.insert(std::make_pair<unsigned int, unsigned int>(7,1));
	graph.insert(std::make_pair<unsigned int, unsigned int>(5,6));
	graph.insert(std::make_pair<unsigned int, unsigned int>(6,5));
	graph.insert(std::make_pair<unsigned int, unsigned int>(5,7));
	graph.insert(std::make_pair<unsigned int, unsigned int>(7,5));
	graph.insert(std::make_pair<unsigned int, unsigned int>(8,7));
	graph.insert(std::make_pair<unsigned int, unsigned int>(7,8));
	graph.insert(std::make_pair<unsigned int, unsigned int>(4,8));
	graph.insert(std::make_pair<unsigned int, unsigned int>(8,4));
	graph.insert(std::make_pair<unsigned int, unsigned int>(8,9));
	graph.insert(std::make_pair<unsigned int, unsigned int>(9,8));
	graph.insert(std::make_pair<unsigned int, unsigned int>(9,10));
	graph.insert(std::make_pair<unsigned int, unsigned int>(10,9));
	list<list<unsigned int>* > routes;
	UIntUIntMap our_reverse;
	UIntUIntMap target_reverse;
	UIntSet intersections;
	boost::asio::lsr::router::findIntersections(graph, 1, 8, 4, intersections,
	                                            our_reverse, target_reverse);
}